#include <algorithm>
#include "link.h"
#include "sdk/misc.h"
using namespace std;

namespace skill {


    bool Link::use(Game *g, const vector<string> &params) {
        try {
            Entity *node = g->entityManager.findEntityByID(params[0]);
            Entity *node_out = g->entityManager.findEntityByID(params[1]);
            node->properties["link_out"] += params[1] + "|";
            node_out->properties["link_in"] += params[0] + "|";
            return true;
        } catch (exception) {
            return false;
        }
    }

    bool Link::canUse(Game *g, const vector<string> &params) {
        try {
            Entity *node = g->entityManager.findEntityByID(params[0]);
            if (node == nullptr) return false;
            if (g->entityManager.findEntityByID(params[1]) == nullptr) return false;
            string &linkOutStr = node->properties["link_out"];
            vector<string> outSet;
            SplitString(linkOutStr, outSet, "|");
            int linkOutCount = 0;
            for (string &outNode:outSet) if (outNode.size() > 0){
                if (outNode == params[1])
                    return false;
                ++ linkOutCount;
            }
            if (linkOutCount + 1 > stoi(origin->properties["outdeg"]))
                return false;
            Actor* target = dynamic_cast<Actor*>(g->entityManager.findEntityByID(params[1]));
            if (getSquaredEuclideanDist((dynamic_cast<Actor*>(node))->pos(), target->pos()) > SplitRange)
                return false;
            return true;
        } catch (exception) {
            return false;
        }
    }

    bool Disconnect::canUse(Game *g, const vector<string> &params) {
        try {
            //forgive me such silly “canuse”
            Entity *node = origin;
            Entity *node_out = g->entityManager.findEntityByID(params[0]);
            if (node_out == nullptr) return false;
            string link_out = node->properties["link_out"];
            string link_in = node_out->properties["link_in"];
            if (link_out.find("|" + node_out->id + "|") == string::npos ) return false;
            if (link_in.find("|" + node->id + "|") == string::npos) return false;
            return true;
        } catch (exception) {
            return false;
        }
    }

    bool Disconnect::use(Game *g, const vector<string> &params) {
        try {
            //forgive me such silly “use”
            Entity *node = origin;
            Entity *node_out = g->entityManager.findEntityByID(params[0]);
            string link_out = node->properties["link_out"];
            string link_in = node_out->properties["link_in"];
            string nodeStr = "|" + node->id + "|";
            string node_outStr = "|" + node_out->id + "|";
            node->properties["link_out"] = link_out.replace(link_out.find(node_outStr), node_outStr.size(), "|");
            node_out->properties["link_in"] = link_in.replace(link_in.find(nodeStr), nodeStr.size(), "|");
            return true;
        } catch (exception) {
            return false;
        }
    }

    bool Connect::use(Game *g, const vector<string> &params) {
        try {
            if (origin->hasSkill("LinktimeBuff" + params[0])) {
                return false;
            }
            origin->learnSkill(new skill::LinktimeBuff(params[0]));
            return true;
        } catch (exception) {
            return false;
        }
    }

    bool Connect::canUse(Game *g, const vector<string> &params) {
        if (params.size() < 1) return false;
        if (g->entityManager.findEntityByID(params[0]) == nullptr) return false;
        try {
            if (origin->hasSkill("LinktimeBuff" + params[0])) {
                return false;
            }
            if (origin->properties["link_out"].find("|" + params[0] + "|") != std::string::npos){
                return false;
            }
            string &linkOutStr = origin->properties["link_out"];
            vector<string> outSet;
            SplitString(linkOutStr, outSet, "|");
            int linkOutCount = 0;
            for (string &outNode:outSet) if (outNode.size() > 0){
                    if (outNode == params[0])
                        return false;
                    ++ linkOutCount;
                }
            if (linkOutCount + 1 > stoi(origin->properties["outdeg"]))
                return false;
            Actor* target = dynamic_cast<Actor*>(g->entityManager.findEntityByID(params[0]));
            if (origin->id == target->id)
                return false;
            if (getSquaredEuclideanDist((dynamic_cast<Actor*>(origin))->pos(), target->pos()) > SplitRange)
                return false;
            return true;
        } catch (exception) {
            return false;
        }
    }

    bool SetTransfer::use(Game *g, const vector<string> &params) {
        try {
            g->linkManager->setTransfer();
            return true;
        } catch (exception) {
            return false;
        }
    }

    bool SetTransfer::canUse(Game *g, const vector<string> &params) {
        return true;
    }

    bool Transfer::use(Game *g, const vector<string> &params) {
        try {
            g->linkManager->addTransfer({origin->id, params[0], params[1]});
            return true;
        } catch (exception) {
            return false;
        }
    }

    bool Transfer::canUse(Game *g, const vector<string> &params) {
        try {
            int transfer_num = stoi(params[1]);
            if (origin->properties["link_out"].find("|" + params[0] + "|") != string::npos) return true;
            return false;
        } catch (exception) {
            return false;
        }
    }
}